import { Component, ComponentOption, Scope } from "../ioc";
import { Metadata_Key } from "../../constants";
import {
  MethodAfterAdvice,
  MethodAfterReturningAdvice,
  MethodAfterThrowingAdvice,
  MethodAroundAdvice,
  MethodBeforeAdvice
} from "../common";

/** 增强类型 */
export enum AdviceType {
  /** 方法前置增强，需要实现 MethodBeforeAdvice 接口 */
  MethodBeforeAdvice,
  /** 方法正常执行后的后置增强，需要实现 MethodAfterReturningAdvice 接口 */
  MethodAfterReturningAdvice,
  /** 方法执行异常时的后置增强，需要实现 MethodAfterThrowingAdvice 接口 */
  MethodAfterThrowingAdvice,
  /** 方法执行完成之后的增强(无论方法执行是否发生异常)，需要实现 MethodAfterAdvice 接口 */
  MethodAfterAdvice,
  /** 方法执行前后的环绕增强，需要实现 MethodAroundAdvice 接口 */
  MethodAroundAdvice,
}

/** Aspect装饰器配置 */
export interface AspectOption extends Omit<ComponentOption, "id" | "enableAop"> {
  /** 增强类型，使用不同增强类型需要实现对应的接口 */
  type: AdviceType;
}

/** Aspect装饰器定义 */
export interface AspectDecorator {
  (option: AspectOption): ClassDecorator;
}

/** Aspect装饰器 */
const Aspect = <AspectDecorator>function (option: AspectOption): ClassDecorator {
  // Aspect装饰器默认配置
  const defaultAspectOption: Partial<AspectOption> = {
    scope: Scope.Singleton,
  };
  // Aspect装饰器配置
  const opt: AspectOption = {...defaultAspectOption, ...option};
  // 返回Class装饰器
  return (target: Function) => {
    let id: symbol | undefined;
    switch (opt.type) {
      case AdviceType.MethodBeforeAdvice:
        id = MethodBeforeAdvice;
        break;
      case AdviceType.MethodAfterReturningAdvice:
        id = MethodAfterReturningAdvice;
        break;
      case AdviceType.MethodAfterThrowingAdvice:
        id = MethodAfterThrowingAdvice;
        break;
      case AdviceType.MethodAfterAdvice:
        id = MethodAfterAdvice;
        break;
      case AdviceType.MethodAroundAdvice:
        id = MethodAroundAdvice;
        break;
      default:
        console.warn("AOP @Aspect装饰器未能生效，type配置错误:", opt.type);
        return;
    }
    if (!id) {
      console.warn("AOP @Aspect装饰器未能生效，type配置错误:", opt.type);
      return;
    }
    Component({enableAop: false, scope: opt.scope, id})(target);
    // 标记当前组件是AOP组件 - 增加元数据(Aspect Metadata)
    Reflect.defineMetadata(Metadata_Key.aspect, true, target);
  };
};

export {
  Aspect,
}
